package emulator.GUI.display;

import java.awt.BorderLayout;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

import com.jhe.hexed.ClipboardInterface;
import com.l2fprod.common.swing.StatusBar;

import emulator.GUI.PersistentFrame;
import emulator.GUI.keyboard.VicKeyboardMap;
import emulator.GUI.util.Clipboard;
import emulator.cbm.basic.BasicHelper;
import emulator.hardware.CPU;
import emulator.hardware.clock.Clock;

@SuppressWarnings("serial")
public class VicFrame extends PersistentFrame {
	private VicKeyboardMap keyboard_map;
	private VicCanvas canvas;
	private JPopupMenu popup;
	private ClipboardInterface clipboard = new Clipboard();
	private StatusBar status_bar;
	
	public VicFrame(String appName, CPU cpu, Clock clock, VicKeyboardMap keyboard_map) throws HeadlessException {
		super("Vic Output", appName);
		this.keyboard_map = keyboard_map;
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    setLayout (new BorderLayout ());
	    
        canvas = new VicCanvas(VicDisplay.MAX_SCREEN_WIDTH,VicDisplay.MAX_SCREEN_HEIGHT);
	    canvas.addMouseListener(new MousePopupListener());
	    canvas.addKeyListener(new KeyboardInputListener());
//	    canvas.addKeyListener(new KeyboardInputDirectListener());
		add(canvas, BorderLayout.CENTER);

		status_bar = new StatusBar();
		status_bar.setZones(
				new String[]{"status", "speed"},
				new JComponent[]{new JLabel(), new JLabel()},
				new String[]{"66%","*"});
		add(status_bar, BorderLayout.SOUTH);
		
	    popup = new JPopupMenu();
	    JMenuItem item_clear = new JMenuItem("Clear Buffer");
	    item_clear.setActionCommand("clear");
	    item_clear.addActionListener(new PopupMenuListener());
	    popup.add(item_clear);
	    JMenuItem item_paste = new JMenuItem("Paste", new ImageIcon("images/paste_icon.png"));
		item_paste.setActionCommand("paste");
	    item_paste.addActionListener(new PopupMenuListener());
	    popup.add(item_paste);

	    showStatus("Ready");
		showSpeed("???");
		
		cpu.addRunningStateObserver(new CpuStateObserver());
		clock.addClockSpeedObserver(new ClockSpeedObserver());
	}

	private void showSpeed(String speed_text) {
		((JLabel)status_bar.getZone("speed")).setText("Clock: "+speed_text);
	}

	private void showStatus(String status_text) {
		((JLabel)status_bar.getZone("status")).setText(status_text);
	}

	public void onClear()
	{
		BasicHelper.clearText();
	}

	public void onPaste()
	{
		BasicHelper.enterText(clipboard.getClipboardContents());
	}
	
	// An inner class to check cpu state events.
	class CpuStateObserver implements Observer
	{
		@Override
		public void update(Observable state, Object run_state) {
			showStatus((Boolean)run_state ? "Running" : "Halted");
		}
	}

	// An inner class to check cpu state events.
	class ClockSpeedObserver implements Observer
	{
		@Override
		public void update(Observable state, Object clock_freq) {
			String[] units = new String[] { "Hz", "kHz", "MHz", "GHz", "THz" };
			long freq = (Long)clock_freq;
			long decimals = 0;
			int unit = 0;
			while (freq > 1000)
			{
				unit++;
				if (decimals > 500)
					decimals = (freq % 1000) + 1;
				else
					decimals = (freq % 1000);
				freq /= 1000;
			}
			String speed_string = Long.toString(freq);
			if (decimals > 0)
			{
				String decimals_string = ("000" + Long.toString(decimals));
				speed_string += "."+decimals_string.substring(decimals_string.length()-3);
			}
			speed_string += " "+units[unit];
			showSpeed(speed_string);
		}
	}
	
	// An inner class to check whether mouse events are the popup trigger
	class MousePopupListener extends MouseAdapter
	{
	    public void mousePressed(MouseEvent e) { checkPopup(e); }
	    public void mouseClicked(MouseEvent e) { checkPopup(e); }
	    public void mouseReleased(MouseEvent e) { checkPopup(e); }
	
	    private void checkPopup(MouseEvent e) {
	    	if (e.isPopupTrigger()) {
				popup.show(canvas, e.getX(), e.getY());
	    	}
	    }
	}
	
	class PopupMenuListener implements ActionListener
	{
		public void actionPerformed(ActionEvent event)
		{
			if (event.getActionCommand().equals("clear"))
				onClear();
			else if (event.getActionCommand().equals("paste"))
				onPaste();
		}
	}
	
	class KeyboardInputDirectListener extends KeyAdapter
	{
		/* (non-Javadoc)
		 * @see java.awt.event.KeyAdapter#keyTyped(java.awt.event.KeyEvent)
		 */
		@Override
		public void keyTyped(KeyEvent event) {
			char key = event.getKeyChar();
			if (key != KeyEvent.CHAR_UNDEFINED)
			{
				if (Character.isLowerCase(key))
					putKey(Character.toUpperCase(key));
				else if (Character.isUpperCase(key))
					putKey(Character.toLowerCase(key));
				else
					putKey(key);
			}
			event.consume();
		}

		private void putKey(char key) {
			String buf = new String();
			buf += key;
			BasicHelper.enterText(buf);
		} 
	}
	
	class KeyboardInputListener extends KeyAdapter
	{

		/* (non-Javadoc)
		 * @see java.awt.event.KeyAdapter#keyPressed(java.awt.event.KeyEvent)
		 */
		@Override
		public void keyPressed(KeyEvent event) {
			if (keyboard_map.doKeyboardEvent(event))
				event.consume();
		}

		/* (non-Javadoc)
		 * @see java.awt.event.KeyAdapter#keyReleased(java.awt.event.KeyEvent)
		 */
		@Override
		public void keyReleased(KeyEvent event) {
			if (keyboard_map.doKeyboardEvent(event))
				event.consume();
		}
	}
}
